﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Citi.Scheduler.Domain.Utils;
using System.Threading;
using Citi.Helpers;
using Citi.Scheduler.Domain;
using System.Configuration;
using System.IO;

namespace Citi.Scheduler.ConsoleUi.UpdateServices
{
    class Program
    {
        private const int TimeoutMilliseconds = 30000;
        private static List<int> MachineAlreadyRestarted { get; set; }
        private static string NetworkUser { get { return ConfigurationManager.AppSettings["NetworkUser"]; } }
        private static string NetWorkPassword { get { return ConfigurationManager.AppSettings["NetWorkPassword"]; } }
        private static string ServicePath { get { return ConfigurationManager.AppSettings["ServicePath"]; } }
        private static string ServicePathSource { get { return ConfigurationManager.AppSettings["ServicePathSource"]; } }
        private static string AppConfigServiceSource { get { return ConfigurationManager.AppSettings["AppConfigServiceSource"]; } }

        static void Main(string[] args)
        {
            var machines = SelectMachine();

            MachineAlreadyRestarted = new List<int>();

            try
            {
                Console.WriteLine("{0} Reading Machines", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"));
                //var machines = new Machine().SelectAllActive<Machine>().Where(x => x.ExecutionCenter || x.MonitorCenter || x.SchedulerCenter);

                while (MachineAlreadyRestarted.Count() != machines.Count())
                {
                    foreach (var machine in machines.Where(x => !MachineAlreadyRestarted.Contains(x.Id)).OrderBy(x => x.FullName))
                    {
                        Console.WriteLine("{0} Machine: {1}", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"), machine.HostName);

                        //var routinesInProgress = machine.RoutineExecutions.Where(x => x.InProgress
                        //        && x.LastHistory.Date > DateTime.Now.AddMinutes(-(x.Routine.EstimatedTime + x.Routine.EstimatedTime * x.Routine.PercentageAcceptableVariation / 100)));

                        //if (machine.RoutineInProgress == null)
                        if (!new Util().MachineIsRunning(machine.Id))
                        {
                            CheckService(machine);

                            MachineAlreadyRestarted.Add(machine.Id);
                        }
                        else
                            Console.WriteLine("{0} Is running a routine at this moment", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"));

                        Console.WriteLine();
                    }

                    if (MachineAlreadyRestarted.Count() != machines.Count())
                    {
                        Console.WriteLine("{0} Execution will sleep for 10 minutes.", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"));
                        Thread.Sleep(600000);
                    }
                }

                Console.WriteLine("Press any key to continue...");
                Console.ReadLine();
            }
            catch (Exception e)
            {
                Console.WriteLine("{0} {1}", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"), e.Message);
                Thread.Sleep(600000);
            }
        }

        private static IEnumerable<Machine> SelectMachine()
        {
            Console.WriteLine("Press A to Update All machines or press C to Update Current Machine");

            var machines = new Machine().SelectAllActive<Machine>().Where(x => x.ExecutionCenter || x.MonitorCenter || x.SchedulerCenter);

            switch (Console.ReadKey().KeyChar)
            {
                case 'C':
                case 'c':
                    machines = machines.Where(x => x.HostName.ToUpper() == Environment.MachineName);

                    if (!machines.Any())
                    {
                        Console.WriteLine();
                        Console.WriteLine("Machine not found");
                    }

                    break;
                case 'A':
                case 'a':
                    break;
                default:
                    Console.WriteLine();
                    Console.WriteLine("Wrong options selected");
                    return SelectMachine();
            }

            Console.WriteLine();

            return machines;
        }

        private static void CheckService(Machine machine)
        {
            try
            {
                StopServices(machine);
                var hostname = machine.FullName.Contains("lac") ? machine.HostName : machine.FullName;

                using (NetworkShareAccesser.Access(string.Format(@"\\{0}", hostname), NetworkUser, NetWorkPassword))
                {
                    var targetDirectory = string.Format(ServicePath, hostname.ToUpper());

                    if (hostname.ToUpper().Contains(Environment.MachineName.ToUpper()))
                    {
                        targetDirectory = targetDirectory.ToLower()
                            .Replace(string.Format(@"\\{0}.lac.nsroot.net\c$\", Environment.MachineName.ToLower()), @"C:\")
                            .Replace(string.Format(@"\\{0}.nam.nsroot.net\c$\", Environment.MachineName.ToLower()), @"C:\")
                            .Replace(string.Format(@"\\{0}\c$\", Environment.MachineName.ToLower()), @"C:\");
                    }

                    Console.WriteLine("{0} Backuping old files.", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"));
                    CreateBackup(hostname, targetDirectory);
                    Console.WriteLine("{0} Copying files to {1}", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"), hostname);
                    CopyFiles(ServicePathSource, targetDirectory);
                    CopyAppConfig(targetDirectory);
                }

                StartServices(machine);
            }
            catch (Exception e)
            {
                Console.WriteLine("{0} {1}", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"), e.Message);
            }
        }

        private static void CopyAppConfig(string targetDirectory)
        {
            Console.WriteLine("{0} Copying app.config", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"));

            if (!File.Exists(AppConfigServiceSource))
            {
                Console.WriteLine("{0} App.config not found", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"));
                return;
            }

            File.Copy(AppConfigServiceSource, Path.Combine(targetDirectory, Path.GetFileName(AppConfigServiceSource)), true);
        }

        private static void CreateBackup(string machine, string servicePath)
        {
            var backupPath = Path.Combine(servicePath, string.Format("BKP {0}", DateTime.Today.ToString("yyyyMMdd")));

            if (!Directory.Exists(backupPath))
                Directory.CreateDirectory(backupPath);

            MoveFiles(servicePath, backupPath);
        }

        private static void CopyFiles(string sourceDirectory, string targetDirectory)
        {
            var files = Directory.GetFiles(sourceDirectory).Where(x => Path.GetExtension(x) != ".config");

            foreach (var file in files)
                File.Copy(file, Path.Combine(targetDirectory, Path.GetFileName(file)), true);
        }

        private static void MoveFiles(string sourceDirectory, string targetDirectory)
        {
            var files = Directory.GetFiles(sourceDirectory);

            foreach (var file in files)
            {
                var fileCopy = Path.Combine(targetDirectory, Path.GetFileName(file));

                try
                {
                    if (Path.GetExtension(fileCopy) == ".config")
                    {
                        File.Copy(file, fileCopy, true);
                        continue;
                    }

                    if (File.Exists(fileCopy))
                        File.Delete(fileCopy);

                    File.Move(file, fileCopy);
                }
                catch
                {
                    Thread.Sleep(10000);
                    File.Move(file, fileCopy);
                }
            }
        }

        private static void StopServices(Machine machine)
        {
            var hostname = machine.FullName.Contains("lac") ? machine.HostName : machine.FullName;

            StopService(hostname, Machine.ServiceSchedulerName);
            StopService(hostname, Machine.ServiceMonitorName);
            StopService(hostname, Machine.ServiceExecutionName);
        }

        private static void StopService(string machine, string serviceName)
        {
            try
            {
                WindowsUtil.StopService(serviceName, machine, TimeoutMilliseconds);
                Console.WriteLine("{0} {1} stopped", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"), serviceName);
            }
            catch (Exception e)
            {
                Console.WriteLine("{0} ", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"), e.Message);
            }
        }

        private static void StartServices(Machine machine)
        {
            var hostname = machine.FullName.Contains("lac") ? machine.HostName : machine.FullName;

            StartService(hostname, machine.ExecutionCenter, Machine.ServiceExecutionName);
            StartService(hostname, machine.SchedulerCenter, Machine.ServiceSchedulerName);
            StartService(hostname, machine.MonitorCenter, Machine.ServiceMonitorName);
        }

        private static void StartService(string machine, bool center, string serviceName)
        {
            if (!center) return;

            try
            {
                WindowsUtil.StartService(serviceName, machine, TimeoutMilliseconds);
                Console.WriteLine("{0} {1} started", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"), serviceName);
            }
            catch (Exception e)
            {
                Console.WriteLine("{0} ", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"), e.Message);
            }
        }
    }

    public class Util
    {
        public bool MachineIsRunning(int machineId)
        {
            using (var client = new SchedulerMonitorWcf.MonitorClient())
                return client.IsMachineRunningRoutine(machineId);
        }
    }
}
